package com.ylr.gateway.framework.filter;

import com.ylr.base.common.constant.Constant;
import com.ylr.base.common.enums.ChannelEnum;
import com.ylr.base.common.enums.ModuleEnum;
import com.ylr.base.common.tool.Result;
import com.ylr.gateway.framework.properties.UriConfig;
import com.alibaba.fastjson2.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.Set;


/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2023-04-25 21:29:35
 * className: UriGlobalFilter 请求白名单/黑名单过滤器
 * version: 1.0
 * description:
 */
@Component
public class UriGlobalFilter implements GlobalFilter, Ordered {

    private static final Logger log = LoggerFactory.getLogger(UriGlobalFilter.class);

    @Value("${spring.application.simple-name}")
    private String applicationSimpleName;

    @Resource(name = "uriConfig")
    private UriConfig uriConfig;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求路径
        String uri = exchange.getRequest().getURI().getPath();
        log.debug("uri过滤器 uri={}", uri);
        // 1、黑名单校验
        UriConfig.Config black = uriConfig.getBlack();
        Set<String> set = black.getEquals();
        if (!CollectionUtils.isEmpty(set)) {
            if (set.contains(uri)) {
                log.warn("请求（{}）命中黑名单", uri);
                // 命中黑名单
                return writeResponse(exchange, "很抱歉，您请求的不允许被访问");
            }

        }
        PathMatcher pathMatcher = new AntPathMatcher();

        List<String> list = black.getMatch();
        if (!CollectionUtils.isEmpty(list)) {
            for (String _uri : list) {
                if (pathMatcher.match(_uri, uri)) {
                    // 命中黑名单
                    log.warn("请求（{}）模糊命中黑名单（{}）", uri, _uri);
                    return writeResponse(exchange, "很抱歉，您请求的不允许被访问");
                }
            }
        }

        // 2、白名单校验
        UriConfig.Config white = uriConfig.getWhite();
        set = white.getEquals();
        if (!CollectionUtils.isEmpty(set)) {
            if (set.contains(uri)) {
                log.debug("请求（{}）命中白名单", uri);
                String[] arr = getModuleAndChannel(uri);
                if (Objects.isNull(arr)) {
                    return writeResponse(exchange, "接口路径前缀错误");
                }
                return chain.filter(requestHeaderBuild(exchange, arr));
            }
        }
        list = white.getMatch();
        if (!CollectionUtils.isEmpty(list)) {
            for (String _uri : list) {
                if (pathMatcher.match(_uri, uri)) {
                    log.debug("请求（{}）模糊命中白名单（{}）", uri, _uri);
                    String[] arr = getModuleAndChannel(uri);
                    if (Objects.isNull(arr)) {
                        return writeResponse(exchange, "接口路径前缀错误");
                    }
                    return chain.filter(requestHeaderBuild(exchange, arr));
                }
            }
        }

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // 数值越小，优先级越高
        return -1;
    }

    /**
     * 封装返回数据
     * @param exchange 服务交换机对象
     * @return 返回结果
     */
    private Mono<Void> writeResponse(ServerWebExchange exchange, String message) {
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        serverHttpResponse.setStatusCode(HttpStatus.OK);
        serverHttpResponse.getHeaders().setContentType(MediaType.valueOf("application/json;charset=UTF-8"));
        Result<Object> result = Result.requestFail(message);
        DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONBytes(result));
        return serverHttpResponse.writeWith(Mono.just(buffer));
    }

    /**
     * 请求头增加新的内容
     * @param exchange 请求交换机
     * @return 变更后的请求交换机对象
     */
    private ServerWebExchange requestHeaderBuild(ServerWebExchange exchange, String[] arr) {
        ServerHttpRequest request = exchange.getRequest()
                .mutate()
                .header(Constant.SOURCE, applicationSimpleName)
                .header(Constant.MODULE, arr[0])
                .header(Constant.CHANNEL, arr[1])
                .header(Constant.AUTH_RESULT, Constant.AUTH_PASS)
                .build();
        return exchange.mutate().request(request).build();
    }

    /**
     * 根据path路径获取请求目标服务code和请求所属渠道code
     * @param path 路径
     * @return 请求目标服务code和请求所属渠道code数组
     */
    private String[] getModuleAndChannel(String path) {
        try {
            int index = path.indexOf("/", 1);
            // 获取请求的目标服务code
            String moduleCode = path.substring(1, index);

            index += 1;

            int index2 = path.indexOf("/", index);
            // 获取请求的所属渠道code
            String channelCode = path.substring(index, index2);

            moduleCode = Objects.requireNonNull(ModuleEnum.getModuleEnum(moduleCode)).getCode();
            channelCode = Objects.requireNonNull(ChannelEnum.getChannelEnum(channelCode)).getCode();

            return new String[] { moduleCode, channelCode };
        } catch (Exception e) {
            log.error("路由解析失败。message={}", e.getMessage(), e);
            return null;
        }
    }
}
